$(document).ready(function (){
	// $('#container').layout();
	//init sketch pane
	var pane = document.getElementById('pane');
	var pwidth = $(pane).width();
	var pheight = $(pane).height()-5;
	if (5*pwidth<8*pheight){
		pheight = 5*pwidth/8.0;
	}else{
		pwidth = 8*pheight/5.0;
	}
	var p = Raphael('svg_wrapper',pwidth,pheight);
	var psvg = pane.firstChild;

	//operate stack and trash stack
	//used to undo and redo
	var operate = new Array();
	operate.count = 0;
	var trash = new Array();

	//visible area
	var vleft = 0;
	var vtop = 0;
	var vwidth = 1600;
	var vheight = 1000;
	p.setViewBox(vleft, vtop, vwidth, vheight);
	var paneOffset = $(pane).offset();
	$("#trash_can_wrapper").offset({ top: paneOffset.top+pheight - 100, left: paneOffset.left+pwidth - 100 })
	//gestures storage
	var gestures = [];

	//client' s memberID
	var memberID = null;
	var elemCnt = 0;
	var isRawExit = true;

	//Glogal states
	var stroke_width = 5;
	var fill = "none";
	var fill_opacity = 0.5;
	var color = "#000";
	var tool = "pen";
	var pressing = false;
	var right_pressing = false;
	var adding = false;
	var targetid = null;
	var sketches = p.set();
	//var textSet = p.set();
	var imageSet = p.set();
	var zoom, ori_distance; //for touch gesture scaling 

	//store member's nicknames
	var member_nickname = new Array();

	//init websocket
	var s = new WebSocket("ws://"+window.location.host+"/socket/"+type+"/"+sessionid);
	s.onopen = function(e) {
		console.log("WebSocket opened");
	}
	s.onclose = function(e) {
		console.log("WebSocket closed");
	}

	s.onmessage = function(e) {
		console.log(e.data);
		message_handler(JSON.parse(e.data));
	}

	//mask right click
	document.oncontextmenu = function() {
		return false;
	}


/**********************************************************************************/
/*                                 TOOL FUNCTIONS                                 */
/**********************************************************************************/
	/*
	 * computes cubic bezier coefficients to generate a smooth
	 * line through specified points. couples with SVG graphics 
	 * for interactive processing.
	 *
	 * For more info see:
	 * http://www.particleincell.com/2012/bezier-splines/ 
	 *
	 * Lubos Brieda, Particle In Cell Consulting LLC, 2012
	 * you may freely use this algorithm in your codes however where feasible
	 * please include a link/reference to the source article
	 */ 
	function computeControlPoints(K)
	{
		p1=new Array();
		p2=new Array();
		n = K.length-1;
		
		/*rhs vector*/
		a=new Array();
		b=new Array();
		c=new Array();
		r=new Array();
		
		/*left most segment*/
		a[0]=0;
		b[0]=2;
		c[0]=1;
		r[0] = K[0]+2*K[1];
		
		/*internal segments*/
		for (i = 1; i < n - 1; i++)
		{
			a[i]=1;
			b[i]=4;
			c[i]=1;
			r[i] = 4 * K[i] + 2 * K[i+1];
		}
				
		/*right segment*/
		a[n-1]=2;
		b[n-1]=7;
		c[n-1]=0;
		r[n-1] = 8*K[n-1]+K[n];
		
		/*solves Ax=b with the Thomas algorithm (from Wikipedia)*/
		for (i = 1; i < n; i++)
		{
			m = a[i]/b[i-1];
			b[i] = b[i] - m * c[i - 1];
			r[i] = r[i] - m*r[i-1];
		}
	 
		p1[n-1] = r[n-1]/b[n-1];
		for (i = n - 2; i >= 0; --i)
			p1[i] = (r[i] - c[i] * p1[i+1]) / b[i];
			
		/*we have p1, now compute p2*/
		for (i=0;i<n-1;i++)
			p2[i]=2*K[i+1]-p1[i+1];
		
		p2[n-1]=0.5*(K[n]+p1[n-1]);
		
		return {p1:p1, p2:p2};
	}

	function get_nickname_by_id (id) {
		return member_nickname[id];
	}
/**********************************************************************************/
/*                                  TOOLBAR INIT                                  */
/**********************************************************************************/
	$("#toolbar").buttonset()
	$("#toolbar>li>a").click(function(e) {
		e.stopPropagation();
		var nowTool = this.id.split('_')[0];
		if (nowTool !== "hand") {
			//imageSet.forEach(function(el) {
				//if (el.holderId) {
					//p.getById(el.holderId).hide();
				//}
			//});
			for (var i = 0; i < imageSet.length; i++) {
				p.getById(imageSet[i].holderId).hide();
			};
			
		}
		else {
			for (var i = 0; i < imageSet.length; i++) {
				p.getById(imageSet[i].holderId).show();
			};
			//imageSet.forEach(function(el) {
				//if (el.holderId) {
					//updateholder(el);
					//p.getById(el.holderId).show();
				//}
			//});
		}
		if (nowTool !== "color" && nowTool !== "stroke" && nowTool !== "redo" && nowTool !== "undo") {
			tool = nowTool;
		}
		if(tool=="hand"){
			sketches.drag(dragmove_handler,dragstart_handler,dragend_handler);
			sketches.forEach(function(elem) {
				if (elem.type === "text") {
					elem.dblclick(dblclick_handler);
				}
			});
			//textSet.dblclick(dblclick_handler);
			//imageSet.hover(hoverin_handler, hoverout_handler);
			
			// sketches.unhover();
			// sketches.hover(
			// 	function(){
			// 		// this.glowset.remove()
			// 		this.glowset = p.set();
			// 		this.glowset.push(this.glow());
			// 	},
			// 	function(){
			// 		this.glowset.remove();
			// 	})
		}else{
			sketches.undrag();
			sketches.undblclick(dblclick_handler);
			//textSet.undblclick(dblclick_handler);
			//imageSet.unhover(hoverin_handler, hoverout_handler);
		}
		//FIXME
	}).hover(function() {
		$(this).next().show();
	},		function() {
		$(this).next().hide();
	})
	$("ul.dropdown").hover(function() {
		$(this).show();
	},		function() {
		$(this).hide();
	})

	$("#toolbar>li>ul.dropdown").hide()
	$("#toolbar>li>ul.dropdown>li>a").click(function() {
		// if (this.id.split('_').[1]=='tool') {
			var nowTool = this.id.split('_')[0];
			var nowToolFather = $(this).parent().parent().prev()[0].id;
			if (nowToolFather !== "color" && nowToolFather !== "stroke") {
				tool = nowTool;
			}
		// }
		//CHANGED
		//if (this.id.indexOf("filled") > -1){
		//	fill = color;
		//}else{
		//	fill = "none";
		//}
		//FIXME
	})

	$("#insertTextModal").hide();
	$("a.inModal").button();
	$("#insertTextSubmit").click(function() {
		if($("#insertText").val()==''){
			return;
		}
		insertTextModal.param.insertText = $("#insertText").val();
		insertTextModal.isSubmit = true;
		TINY.box.hide();
	})
	$("#insertTextCancel").click(function() {
		insertTextModal.isSubmit = false;
		TINY.box.hide();
	})
	$("#color").next().find(">li>a").click(function(){
		color = "#" + this.id.split('_')[1];
	})

	$("#stroke").next().find(">li>a").click(function(){
		stroke_width = Number(this.id.split('_')[1]);
	})

	//CHANGED 
	//add undo and redo
	$("#undo").click(function() {
		execute_send("undo",{});
	});
	$("#redo").click(function() {
		execute_send("redo",{})
	});


/**********************************************************************************/
/*                                   SIDEBAR INIT                               */
/**********************************************************************************/

	$(".tabs").tabs();
	$(".btn").button();

	$("#chat_input").keyup(function(e) {
		if(e.keyCode == 13){
			if (this.value !== ""){
				execute_send("chat_come", {content:this.value, mid:memberID});
				this.value = '';
			}
		}
	})

/**********************************************************************************/
/*                                   TRASH_CAN INIT                               */
/**********************************************************************************/
	var cleanupModal = document.getElementById('cleanupModal')
	$("#cleanupModal").hide();
	$("#cleanupSubmit").click(function(){
		isSubmit = true;
		TINY.box.hide();
	});
	$("#cleanupCancel").click(function() {
		isSubmit = false;
		TINY.box.hide();
	});
	$("#trash_can").click(function() {
		$("#cleanupModal").show();
		TINY.box.show({html:  cleanupModal,
				closejs: function() {
					if (isSubmit) {
						execute_send('cleanup', {});
					}
				}, openjs: function() {
					isSubmit = false;
				}
		});
	});
	$("#trash_can").hover(function() {
		if (tool=="hand" && pressing) {
			this.src = "../static/img/trash_can_open_100.png";
		}
	},	function() {
		if (tool=="hand" && pressing) {
			this.src = "../static/img/trash_can_100.png";
		};
	}).mouseup(function() {
		this.src = "../static/img/trash_can_100.png";
	})
	document.getElementById("trash_can").draggable = false;

/**********************************************************************************/
/*                                   CHAT INIT                                    */
/**********************************************************************************/
	var inviteModal = document.getElementById("inviteModal");
	var finishChatModal = document.getElementById("finishChatModal");
	$("#inviteModal").hide();
	$("#finishChatModal").hide();
	$("#finishChatSubmit").click(function() {
		console.log("window close");
		canvg('convert_canvas',document.getElementById('svg_wrapper').innerHTML,{renderCallback: function() {
			var img = document.getElementById('convert_canvas').toDataURL("image/png");
			document.getElementById('shareForm_cover').value = img;
			isRawExit = false;
			$.post("/update_cover/",{cover:img, sid:sessionid}, function(){
				document.getElementById('shareForm').submit();
			})
		}});
	});
	$("#finishChatCancel").click(function() {
		TINY.box.hide();
	});
	$("#invite").click(function() {
		$("#inviteModal").show();
		TINY.box.show({html:'邀请你的朋友：<input type="text" size=40 value="http://'+window.location.host+'/'+type+'/'+sessionid+'">',});
	});
	$("#finish").click(function() {
		$("#finishChatModal").show();
		TINY.box.show({html:finishChatModal});
	});
/**********************************************************************************/
/*                                   OTHER INIT                                   */
/**********************************************************************************/
	function gestureToFront() {
		for (var i = 0; i < gestures.length; i++) {
			if(gestures[i]) gestures[i].toFront();
		};
	}

	function pushNewOperate (obj) {
		operate.push(obj);
		gestureToFront();
		trash = new Array();
	}

	var insertTextModal = document.getElementById('insertTextModal');

	//Detect UserAgent
	var sUserAgent = window.navigator.userAgent;
	var isFirefox = sUserAgent.indexOf("Firefox") > -1;
	var isiPad = (sUserAgent.indexOf("iPad") > -1) || (sUserAgent.indexOf("iPhone") > -1); 


	// To calculate the real coordinates
	function vx(x){
		return parseInt(vleft+x/pwidth*vwidth);
	}

	function vy (y) {
		return parseInt(vtop+y/pheight*vheight);
	}

	function vdx (dx) {
		return parseInt(dx/pwidth*vwidth);
	}

	function vdy (dy) {
		return parseInt(dy/pheight*vheight);
	}

	function updateholder (elem) {
		var box = elem.getBBox();
		p.getById(elem.holderId).show().attr({"cx": box.x2, "cy": box.y2});
	}



	//Operation Excution(c:command, p:param)
	function execute_send (command, param) {
		var message = {c:command, p:param};
		option[command](param);
		//FIXME CHANGED
		s.send(JSON.stringify(message));
	}

	function message_handler (message) {
		option[message.c](message.p);
	}

/**********************************************************************************/
/*                                 EVENTS HANDLERS                                */
/**********************************************************************************/
	// if (!isiPad) {
		pane.onmousedown = mousedown_handler;
		pane.onmousemove = mousemove_handler;
		pane.onmouseup = mouseup_handler;
		pane.addEventListener('dragover',dragOver_handler,false);
		pane.addEventListener("drop",paneDrop_handler,false);
	// }else if(isiPad){
		pane.addEventListener("touchstart", touchstart_handler);
		pane.addEventListener("touchmove", touchmove_handler);
		pane.addEventListener("touchend", touchend_handler);
		// $(pane).hammer().bind("transform", transform_handler).bind("transformstart",transformstart_handler);
	// }

		// For zooming
	if (!isiPad) {
		$(pane).bind('mousewheel', function(event, delta, deltaX, deltaY) {
			event.preventDefault();
			var rate = 0.1;
			var x = vx(event.pageX-paneOffset.left);
			var y = vy(event.pageY-paneOffset.top);
			command = "setViewBox";
			var width = vwidth*(1+delta*rate/2);
			if (width < 160 || width > 16000) {
				return;
			}
			param = {left:x-(x-vleft)*(1+delta*rate/2)
					,top:y-(y-vtop)*(1+delta*rate/2)
					,width:vwidth*(1+delta*rate/2)
					,height:vheight*(1+delta*rate/2)};
			execute_send(command,param);


		});
	}else{
		//FIXME FOR IPAD
	}

	//Event handler
	function mousedown_handler (e) {
		if (e.button == 2) {
			if (!right_pressing) {
				right_pressing = true;
				var param = {x: vx(e.pageX - paneOffset.left), y: vy(e.pageY - paneOffset.top), id:memberID};
				execute_send('gesture_start', param);
			}
			e.returnvalue = false;
			return false;
		} else if(!pressing && tool!="hand" && tool!="text"){
			// if (e.srcElement != psvg) return;
			var param = {x:vx(e.pageX-paneOffset.left), y:vy(e.pageY-paneOffset.top), w:stroke_width, c:color, targetid:String(memberID)+':'+String(elemCnt++)};
			var command = "";
			command = tool+"_start";
			pressing = true;
			adding = true;
			execute_send(command,param);
		}
	}

	function mousemove_handler (e) {
		if (right_pressing) {
			var param = {x: vx(e.pageX - paneOffset.left), y: vy(e.pageY - paneOffset.top), id:memberID};
			execute_send('gesture_move', param);
			e.returnvalue = false;
		}
		if(pressing && tool!="hand" && tool!="text"){
			// if (e.srcElement != psvg) return;
			var param = {x:vx(e.pageX-paneOffset.left), y:vy(e.pageY-paneOffset.top), targetid:targetid};
			command = tool+"_move";
			execute_send(command,param);
		}	
	}

	function mouseup_handler (e) {
		if (e.button == 2) {
			var param = {id: memberID};
			execute_send('gesture_end', param);
			right_pressing = false;
			e.returnvalue = false;
			return false;
		} else {
			if(pressing && tool!="hand" && tool!="text"){
				var param = {targetid:targetid};
				command = tool+"_end";
				pressing = false;
				execute_send(command,param);
			}else if(tool=="text"){
				var param = {x:vx(e.pageX-paneOffset.left), y:vy(e.pageY-paneOffset.top), c:color, targetid:String(memberID)+':'+String(elemCnt++)};
				$("#insertTextModal").show();
				//insertTextModal.param = param;
				var areaHeigt = 50;
				TINY.box.show({html:insertTextModal,boxid:"insert_text_box", top: e.pageY-paneOffset.top - areaHeigt / 2, left: e.pageX-paneOffset.left, opacity:0,
				mclosejs:function() {
					param.insertText = $("#insertText").val();
					var command = "insert_text";
					execute_send(command,param);
				},openjs:function(){
					$("#insertText").val("");
					$("#insertText").focus()
					.keyup(function(event){
						//CHAGNED
					    //if(event.keyCode == 13){
						//$("#insertTextSubmit").click();
					    //}
					}) ;
				}});
			}
		}
	}

	function touchstart_handler (e) {
		e.preventDefault();
		if (e.touches.length==1){
		// if (e.touches.item(0) == e.targetTouches.item(0)){
			var touch = e.touches[0];
			adding = true;
			// alert(touch.pageX-offset.left);
			// alert(touch.pageX);
			if(!pressing && tool!="hand" && tool!="text"){
				var param = {x:vx(touch.pageX-paneOffset.left), y:vy(touch.pageY-paneOffset.top), w:stroke_width, c:color, targetid:String(memberID)+':'+String(elemCnt++)};
				var command = "";
				command = tool+"_start";
				pressing = true;
				execute_send(command,param);
			}
		}else if(e.touches.length>1){
			pressing = false;
			if(e.touches.length==2){
				zoom = 1.0;
				var x1=e.touches[0].pageX;
				var y1=e.touches[0].pageY;
				var x2=e.touches[1].pageX;
				var y2=e.touches[1].pageY;
				ori_distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
			}
		}
	}

	function touchmove_handler (e) {
		e.preventDefault();
		if (e.touches.length==1){
			var touch = e.touches[0];
			if(pressing && tool!="hand" && tool!="text"){
				var param = {x:vx(touch.pageX-paneOffset.left), y:vy(touch.pageY-paneOffset.top), targetid:targetid};
				command = tool+"_move";
				execute_send(command,param);
			}
		}else if(e.touches.length==2){
			
			var x1=e.touches[0].pageX;
			var y1=e.touches[0].pageY;
			var x2=e.touches[1].pageX;
			var y2=e.touches[1].pageY;
			var x = vx((x1+x2)/2-paneOffset.left);
			var y = vy((y1+y2)/2-paneOffset.top);
			var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
			var scale = distance / ori_distance;
			var ratio = scale / zoom;
			command = "setViewBox";
			var width = vwidth / ratio;
			if (width < 160 || width > 16000) {
				return ;
			}
			param = {left:x-(x-vleft)/ratio
					,top:y-(y-vtop)/ratio
					,width:vwidth/ratio
					,height:vheight/ratio};
			zoom = scale;
			execute_send(command,param);
		}
	}

	function touchend_handler (e) {
		e.preventDefault();
		if (e.touches.length==0){
			if(pressing && tool!="hand" && tool!="text"){
				var param = {targetid:targetid};
				command = tool+"_end";
				pressing = false;
				execute_send(command,param);
			}else if(tool=="text"){
				var touch = e.changedTouches[0];
				var param = {x:vx(touch.pageX-paneOffset.left), y:vy(touch.pageY-paneOffset.top), c:color};
				$("#insertTextModal").show();
				insertTextModal.param = param;
				var areaHeigt = 200;
				TINY.box.show({html:insertTextModal,boxid:"insert_text_box",  top: touch.pageY-paneOffset.top - areaHeigt / 2, left: touch.pageX-paneOffset.left,  opacity:0,
				mclosejs:function() {
					insertTextModal.param.insertText = $("#insertText").val();
					var param = insertTextModal.param;
					var command = "insert_text";
					execute_send(command,param);
				},openjs:function(){
					$("#insertText").val("");
					$("#insertText").focus()
					.keyup(function(event){
						//CHAGNED
					    //if(event.keyCode == 13){
						//$("#insertTextSubmit").click();
					    //}
					}) ;
				}});
			}
		}
	}

	function dragstart_handler (x,y,e) {
		if(tool=='hand' && !pressing && (isiPad || e.button == 0)){
			var command = 'drag_start';
			var param = {x:vx(x),y:vy(y),id:this.id};
			execute_send(command,param);
			pressing = true;
		}
	}

	function dragmove_handler (dx,dy,x,y,e) {
		if(tool=='hand' && pressing){
			var command = 'drag_move';
			//CHAGNED
			var param = {x: vx(x), y: vy(y), id: this.id};
			//var param = {dx:vdx(dx),dy:vdy(dy),id:this.id};
			execute_send(command,param);
		}
	}

	function dragend_handler (e){
		if(tool=='hand' && pressing && (isiPad || e.button == 0)){
			var command = 'drag_end';
			var param = {id:this.id, px:e.pageX-paneOffset.left, py: e.pageY-paneOffset.top};
			if (param.px > (pwidth - 100) && param.py > (pheight - 100)) {
				command = 'delete';
				execute_send(command, param);
			}
			else {
				execute_send(command,param);
			}
			pressing = false;
		}
	}

	function dragOver_handler (e) {
		e.stopPropagation();
		e.preventDefault();
		e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
	}

	function paneDrop_handler (e) {
		e.stopPropagation();
		e.preventDefault();
		var files = e.dataTransfer.files; // FileList object
		var offsetX = vx(e.pageX-paneOffset.left);
		var offsetY = vy(e.pageY-paneOffset.top);

	    // Loop through the FileList and render image files as thumbnails.
	    for (var i = 0, f; f = files[i]; i++) {

			// process image files.
			if (f.type.match('image.*')) {
				var reader = new FileReader();
				// Closure to capture the file information.
				reader.onload = (function(theFile) {
					return function(e) {
						var img = new Image;
						img.src = e.target.result;
						// img.addEventListener("dragstart",function(e){
						// 	e.dataTransfer.setData('image/png',this.src);
						// 	e.dataTransfer.setData('application/json',JSON.stringify({width:img.width,height:img.height}));
						// }, false);
						// document.getElementById('material_folder').insertBefore(span, null);
						img.onload = function() {
							console.log(e.target.result, offsetX, offsetY,img.width,img.height);
							var param = {src:img.src,x:offsetX, x:offsetY,width:vdx(img.width),height:vdx(img.height),targetid:String(memberID)+':'+String(elemCnt++)};
							execute_send("insert_image", param);
						}
					};
				})(f);
				// Read in the image file as a data URL.
				reader.readAsDataURL(f);
			}else{
				// FIXME FOR PDF
				if(window.FormData) {
					var formData = new FormData(document.getElementById('upload_form'));
					formData.append('x',offsetX);
					formData.append('y',offsetY);
					formData.append('targetid',String(memberID)+':'+String(elemCnt++))
					formData.append('upload', f);
					var xhr = new XMLHttpRequest();
					xhr.open('POST', '/upload/');
					xhr.onload = function () {
						if (xhr.status !== 200) {
							console.log('upload error');
						}
					}
					xhr.send(formData);
				}
			}
	  	}
	}
	function dblclick_handler (e) {
		var param = {x:vx(e.pageX-paneOffset.left), y:vy(e.pageY-paneOffset.top), c:color, targetid: this.id};
		var lastText = this.attr("text");
		TINY.box.show({html:insertTextModal,boxid:"insert_text_box", top: e.pageY-paneOffset.top, left: e.pageX-paneOffset.left, opacity:0,
		mclosejs:function() {
			param.updateText = $("#insertText").val();
			var command = "update_text";
			execute_send(command,param);
		},openjs:function(){
			$("#insertText").val(lastText);
			$("#insertText").focus()
		}});
	}
	//function hoverin_handler(e) {
		//var holder = p.getById(this.holderId);
		//var box = this.getBBox();
		//holder.attr({"cx": box.x2, "cy": box.y2});
		//holder.show();
		//var image = this;
		//holder.drag(function(dx, dy, x, y, e){
			//if(tool=='hand' && pressing){
				//var command = 'updateimage_move';
				//var param = {x: vx(x), y: vy(y), id: image.id};
				//execute_send(command,param);
			//}
		//}, function(x, y, e) {
			//if(tool=='hand' && !pressing && e.button == 0){
				//var command = 'updateimage_start';
				//var param = {x:vx(x),y:vy(y),id: image.id};
				//execute_send(command,param);
				//pressing = true;
			//}
		//},function(e) {
			//if(tool=='hand' && pressing && e.button == 0){
				//var command = 'updateimage_end';
				//var param = {id: image.id, x:e.offsetX, y: e.offsetY};
				//execute_send(command,param);
				//pressing = false;
			//}
		//});
	//}
	//function hoverout_handler(e) {
		//var holder = p.getById(this.holderId);
		////holder.hide();
	//}
/**********************************************************************************/
/*                                       OPTIONS                                  */
/**********************************************************************************/

	//operation steps
	var option = new Array();

	option['set_memberID'] = function (param) {
		memberID = param.mid;
		execute_send("set_member_nickname", {mid:memberID, nickname:nickname})
		gestures[memberID] = new gesture(memberID);
		gestures[memberID].initiate();
		//for online member
		// $("#online_list").append('<div id="online_'+String(param.mid)+'"><span style="color:'+getColorViaId(param.mid)+'">⚫</span>'+get_nickname_by_id(param.mid)+'</div>');
	}

	option['set_member_nickname'] = function (param) {
		member_nickname[param.mid] = param.nickname;
		$("#online_list").append('<div id="online_'+String(param.mid)+'"><span style="color:'+getColorViaId(param.mid)+'">∎</span>'+get_nickname_by_id(param.mid)+'</div>');
	}

	option['chat_come'] = function (param) {
		$("#chat_list").append("<div>"+get_nickname_by_id(param.mid)+":"+param.content+"</div>")
	}

	option['new_member'] = function (param) {
		//for gestures
		gestures[param.mid] = new gesture(param.mid);
		gestures[param.mid].initiate();
		//for online member

	}

	option['member_leave'] = function (param) {
		//for online member
		$('#online_'+String(param.mid)).remove();

	}

	option['pen_start'] = function (param) {
		var newpath = p.path('M'+String(param.x)+','+String(param.y));
		newpath.patharrayX = [];
		newpath.patharrayY = [];
		newpath.patharrayX[0] = param.x;
		newpath.patharrayY[0] = param.y;
		newpath.patharrayP = 0;
		
		newpath.attr('stroke',param.c).attr('stroke-width', param.w);
		newpath.attr('stroke-linecap', 'round');
		newpath.attr('stroke-linejoin', 'round');
		newpath.id = param.targetid;
		if (adding) {
			targetid = newpath.id;
			adding = false;
		}
		//CHANGED
	}

	option['pen_move'] = function (param) {
		var newpath = p.getById(param.targetid);
		newpath.patharrayP++;
		newpath.patharrayX[newpath.patharrayP] = param.x;
		newpath.patharrayY[newpath.patharrayP] = param.y;
		var newpathString = newpath.attr('path')+'L'+String(param.x)+','+String(param.y);
		newpath.attr('path',newpathString);
	}
	option['pen_end'] = function (param) {
		var newpath = p.getById(param.targetid);
		var px = computeControlPoints(newpath.patharrayX);
		var py = computeControlPoints(newpath.patharrayY);
		var newpathString = 'M'+newpath.patharrayX[0]+','+newpath.patharrayY[0];
		for (var i = 0; i < newpath.patharrayX.length - 1; i++) {
			newpathString+=' C'+px.p1[i]+','+py.p1[i]+' '+px.p2[i]+','+py.p2[i]+' '+newpath.patharrayX[i+1]+','+newpath.patharrayY[i+1];
		}
		newpath.attr('path', newpathString);

		sketches.push(newpath);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newpath.attr();
		c.type = newpath.type;
		pushNewOperate({operation: "new", id: newpath.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['line_start'] = function (param) {
		var newline;
		newline = p.line(param.x,param.y,param.x,param.y, param.w, param.c);
		newline.ox = param.x;
		newline.oy = param.y;
		newline.id = param.targetid;
		if (adding) {
			targetid = newline.id;
			adding = false;
		}
		//CHANGED
	}

	option['line_move'] = function (param) {
		var newline = p.getById(param.targetid);
		var ox = newline.ox;
		var oy = newline.oy;
		newline.linemove(ox,oy,param.x,param.y);
	}

	option['line_end'] = function (param) {
		var newline = p.getById(param.targetid);
		sketches.push(newline);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newline.attr();
		c.type = newline.type;
		pushNewOperate({operation: "new", id: newline.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['onewayArrow_start'] = function (param) {
		var newline;
		newline = p.singlearrow(param.x, param.y, param.x, param.y, param.w, param.c);
		newline.ox = param.x;
		newline.oy = param.y;
		newline.id = param.targetid;
		if (adding) {
			targetid = newline.id;
			adding = false;
		}
		//CHANGED
	}

	option['onewayArrow_move'] = function (param) {
		var newline = p.getById(param.targetid);
		var ox = newline.ox;
		var oy = newline.oy;
		newline.singlearrowmove(ox,oy,param.x,param.y);
	}

	option['onewayArrow_end'] = function (param) {
		var newline = p.getById(param.targetid);
		sketches.push(newline);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newline.attr();
		c.type = newline.type;
		pushNewOperate({operation: "new", id: newline.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['twowayArrow_start'] = function (param) {
		var newline;
		newline = p.doublearrow(param.x,param.y,param.x,param.y,param.w, param.c);
		newline.ox = param.x;
		newline.oy = param.y;
		newline.id = param.targetid;
		if (adding) {
			targetid = newline.id;
			adding = false;
		}
		//CHANGED
	}

	option['twowayArrow_move'] = function (param) {
		var newline = p.getById(param.targetid);
		var ox = newline.ox;
		var oy = newline.oy;
		newline.doublearrowmove(ox,oy,param.x,param.y);
	}

	option['twowayArrow_end'] = function (param) {
		var newline = p.getById(param.targetid);
		sketches.push(newline);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newline.attr();
		c.type = newline.type;
		pushNewOperate({operation: "new", id: newline.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['rectFilled_start'] = function (param) {
		var newrect = p.rect(param.x,param.y,0,0);
		newrect.ox = param.x;
		newrect.oy = param.y;
		newrect.attr("stroke",param.c).attr('stroke-width',param.w).attr('fill', param.c).attr('fill-opacity',fill_opacity);
		newrect.id = param.targetid;
		if (adding) {
			targetid = newrect.id;
			adding = false;
		}
		//CHANGED
	}

	option['rectFilled_move'] = function (param) {
		var newrect = p.getById(param.targetid);
		var x = param.x;
		var y = param.y;
		if(x<newrect.ox){newrect.attr('x',x)};
		if(y<newrect.oy){newrect.attr('y',y)};
		newrect.attr('width',Math.abs(x-newrect.ox));
		newrect.attr('height',Math.abs(y-newrect.oy));
	}

	option['rectFilled_end'] = function (param) {
		var newrect = p.getById(param.targetid);
		sketches.push(newrect);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newrect.attr();
		c.type = newrect.type;
		pushNewOperate({operation: "new", id: newrect.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['rect_start'] = function (param) {
		var newrect = p.rect(param.x,param.y,0,0);
		newrect.ox = param.x;
		newrect.oy = param.y;
		newrect.attr("stroke",param.c).attr('stroke-width',param.w);
		newrect.id = param.targetid;
		if (adding) {
			targetid = newrect.id;
			adding = false;
		}
		//CHANGED
	}

	option['rect_move'] = function (param) {
		var newrect = p.getById(param.targetid);
		var x = param.x;
		var y = param.y;
		if(x<newrect.ox){newrect.attr('x',x)};
		if(y<newrect.oy){newrect.attr('y',y)};
		newrect.attr('width',Math.abs(x-newrect.ox));
		newrect.attr('height',Math.abs(y-newrect.oy));
	}

	option['rect_end'] = function (param) {
		var newrect = p.getById(param.targetid);
		sketches.push(newrect);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newrect.attr();
		c.type = newrect.type;
		pushNewOperate({operation: "new", id: newrect.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['ellipse_start'] = function (param) {
		var newellipse = p.ellipse(param.x,param.y,0,0);
		newellipse.ox = param.x;
		newellipse.oy = param.y;
		newellipse.attr("stroke",param.c).attr('stroke-width',param.w);
		newellipse.id = param.targetid;
		if (adding) {
			targetid = newellipse.id;
			adding = false;
		}
		//CHANGED
	}

	option['ellipse_move'] = function (param) {
		var newellipse = p.getById(param.targetid);
		var x = param.x;
		var y = param.y;
		var y = param.y;
		newellipse.attr({'ry': Math.abs(y-newellipse.oy) / 2, 'cy': (y + newellipse.oy) / 2, 'cx': (x + newellipse.ox) / 2, 'rx': Math.abs(x - newellipse.ox) / 2});
	}

	option['ellipse_end'] = function (param) {
		var newellipse = p.getById(param.targetid);
		sketches.push(newellipse);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newellipse.attr();
		c.type = newellipse.type;
		pushNewOperate({operation: "new", id: newellipse.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}
	option['ellipseFilled_start'] = function (param) {
		var newellipse = p.ellipse(param.x,param.y,0,0);
		newellipse.ox = param.x;
		newellipse.oy = param.y;
		newellipse.attr("stroke",param.c).attr('stroke-width',param.w).attr('fill', param.c).attr('fill-opacity',fill_opacity);
		newellipse.id = param.targetid;
		if (adding) {
			targetid = newellipse.id
			adding = false;
		}
		//CHANGED
	}

	option['ellipseFilled_move'] = function (param) {
		var newellipse = p.getById(param.targetid);
		var x = param.x;
		var y = param.y;
		newellipse.attr({'ry': Math.abs(y-newellipse.oy) / 2, 'cy': (y + newellipse.oy) / 2, 'cx': (x + newellipse.ox) / 2, 'rx': Math.abs(x - newellipse.ox) / 2});
	}

	option['ellipseFilled_end'] = function (param) {
		var newellipse = p.getById(param.targetid);
		sketches.push(newellipse);
		if(targetid === param.targetid) targetid = null;
		//CHANGED
		var c = newellipse.attr();
		c.type = newellipse.type;
		pushNewOperate({operation: "new", id: newellipse.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['insert_text'] = function (param) {
		var newtext = p.text(param.x,param.y,param.insertText)
		.attr({"font-family":"serif", "font-size":vdx(30), "fill":param.c, "text-anchor":"start"});
		newtext.id = param.targetid;
		sketches.push(newtext);
		//textSet.push(newtext);
		//CHANGED
		var c = newtext.attr();
		c.type = "text";
		pushNewOperate({operation: "new", id: newtext.id, array_id: operate.count++, obj: c});
		//gestureToFront();
	}

	option['update_text'] = function (param) {
		var targetText = p.getById(param.targetid);
		var lastStr = targetText.attr("text");
		targetText.attr("text", param.updateText);
		if (targetText !== param.updateText) {
			pushNewOperate({operation: "updateText", id: targetText.id, array_id: operate.count++, from: lastStr, to: param.updateText});
		}
	}

	option['insert_image'] = function (param) {
		var newpic = p.image(param.src,param.x,param.y,param.width,param.height).attr("stroke", "black").attr("stroke-width", 4);
		newpic.id = param.targetid;
		var box = newpic.getBBox();
		var holder = p.circle(box.x2, box.y2, Math.max(Math.min(box.height, box.width) / 100, 5)).attr({"fill": "blue", "stroke-width": 3});
		holder.imageId = newpic.id;
		holder.drag(function(dx, dy, x, y, e){
			if(tool=='hand' && pressing){
				var command = 'updateimage_move';
				var param = {x: vx(x - paneOffset.left), y: vy(y - paneOffset.top), id: this.imageId};
				execute_send(command,param);
			}
		}, function(x, y, e) {
			if(tool=='hand' && !pressing && e.button == 0){
				var command = 'updateimage_start';
				var param = {x:vx(x - paneOffset.left),y:vy(y - paneOffset.top),id: this.imageId};
				execute_send(command,param);
				pressing = true;
			}
		},function(e) {
			if(tool=='hand' && pressing && e.button == 0){
				var command = 'updateimage_end';
				var param = {id: this.imageId, x:e.offsetX, y: e.offsetY};
				execute_send(command,param);
				pressing = false;
			}
		});
		holder.hide();
		newpic.holderId = holder.id;
		//holder.hide();
		sketches.push(newpic);
		imageSet.push(newpic);
		var c = newpic.attr();
		c.type = newpic.type;
		var attr = holder.attr();
		attr.type = holder.type;
		pushNewOperate({operation: "new", id: newpic.id, holder_id: holder.id, array_id: operate.count++, obj: c, holder_obj: attr});
		//gestureToFront();
	}

	option['drag_start'] = function (param) {
		var elem = p.getById(param.id);
		// elem.glowset.remove();
		// sketches.unhover();
		//CHANGED
		elem.toFront();
		elem.lastMatrix = elem.matrix;
		elem.lastX = param.x;
		elem.lastY = param.y;
		var d = new Date();
		elem.lastTime = d.getTime();
		if (elem.holderId) {
			p.getById(elem.holderId).hide();
		}
	}

	option['drag_move'] = function (param) {
		var elem = p.getById(param.id);
		//CHANGED
		elem.transform("t" + (param.x - elem.lastX) + "," + (param.y - elem.lastY) + "...");
		elem.lastX = param.x;
		elem.lastY = param.y;
		//elem.attr('x',elem.ox+param.dx).attr('y',elem.oy+param.dy);
	}

	option['drag_end'] = function (param) {
		var elem = p.getById(param.id);
		var d = new Date();
		if (elem.holderId) {
			updateholder(elem);
		}
		if (d.getTime() - elem.lastTime < 200) {
			return;
		}
		pushNewOperate({
				operation: "transform",
				id: elem.id,
				array_id: operate.count++,
				from: elem.lastMatrix,
				to: elem.matrix
		});
		// sketches.hover(
		// 	function(){
		// 		this.glowset = this.glow();
		// 	},
		// 	function(){
		// 		this.glowset.remove();
		// 	})
		// elem.glowset.push(elem.glow());
		// console.log('glow')
	}

	option['delete'] = function (param) {
		var elem = p.getById(param.id);
		elem.transform(elem.lastMatrix.toTransformString());
		var c = elem.attr();
		c.type = elem.type;
		var pushObj = {operation: "delete", id: elem.id, array_id: operate.count++, obj: c};
		if (elem.holderId) {
			p.getById(elem.holderId).hide();
			pushObj.holder_id = elem.holderId;
			imageSet.exclude(elem);
		}
		sketches.exclude(elem);
		elem.remove();
		pushNewOperate(pushObj);
	}

	option['cleanup'] = function (param) {
		var objArray = [];
		var len = sketches.length
		for (var i = 0; i < len; i++) {
			objArray[i] = sketches[i].attr();
			objArray[i].id = sketches[i].id;
			objArray[i].type = sketches[i].type;
			if (sketches[i].holderId) {
				p.getById(sketches[i].holderId).hide();
				objArray[i].holder_id = sketches[i].holderId;
				imageSet.exclude(sketches[i]);
			}
			sketches[i].remove();
		};
		sketches.clear();
		pushNewOperate({operation: "cleanup", array_id: operate.count++, obj_array: objArray});
	}
	option['setViewBox'] = function (param) {
		vleft = param.left;
		vtop = param.top;
		vwidth = param.width;
		vheight = param.height;
		p.setViewBox(param.left,param.top,param.width,param.height);
	}

	option['undo'] = function (param) {
		if (operate.length > 0) {
			var lastElem = operate.pop();
			var elem = p.getById(lastElem.id);
			switch(lastElem.operation) {
			case "new":
				if (lastElem.holder_id) {
					p.getById(lastElem.holder_id).hide();
					imageSet.exclude(elem);
				}
				sketches.exclude(elem);
				elem.remove();
				break;
			case "delete":
				var ele = p.add([lastElem.obj]);
				ele.forEach(function(el) {
					el.id = lastElem.id;
					sketches.push(el);
					if (lastElem.holder_id) {
						var hl = p.getById(lastElem.holder_id).show();
						hl.imageId = el.id;
						el.holderId = lastElem.holder_id;
						imageSet.push(el);
					}
				});
				break;
			case "transform":
				elem.transform(lastElem.from.toTransformString());
				break;
			case "updateText":
				elem.attr("text", lastElem.from);
				break;
			case "updateimage":
				elem.attr({"width": lastElem.from.width, "height": lastElem.from.height});
				updateholder(elem);
				break;
			case "cleanup":
				var eles = p.add(lastElem.obj_array);
				var len = lastElem.obj_array.length;
				for (var i = 0; i < len; i++) {
					eles[i].id = lastElem.obj_array[i].id;
					sketches.push(eles[i]);
					if (lastElem.obj_array[i].holder_id) {
						var hl = p.getById(lastElem.obj_array[i].holder_id).show();
						hl.imageId = eles[i].id;
						eles[i].holderId = hl.id;
						imageSet.push(eles[i]);
					}
				};
				break;
			// if (trash.length > 0 && trash[trash.length - 1].array_id < lastElem.array_id) {
			// 	trash = new Array();
			// };
			}
			trash.push(lastElem);
		}
	}

	option['redo'] = function (param) {
		if (trash.length > 0) {
			var lastTrash = trash.pop();
			var elem = p.getById(lastTrash.id);
			switch(lastTrash.operation) {
			case "new": 
				var ele = p.add([lastTrash.obj]);
				ele.forEach(function(el) {
					el.id = lastTrash.id;
					sketches.push(el);
					if (lastTrash.holder_id) {
						var hl = p.getById(lastTrash.holder_id).show();
						hl.imageId = el.id;
						el.holderId = lastTrash.holder_id;
						imageSet.push(el);
					}
				});
				break;
			case "delete":
				if (lastTrash.holder_id) {
					p.getById(holder_id).hide();
					imageSet.exclude(elem);
				}
				sketches.exclude(elem);
				elem.remove();
				break;
			case "updateText":
				elem.attr("text", lastTrash.to);
				break;
			case "transform": 
				elem.transform(lastTrash.to.toTransformString());
				break;
			case "updateimage":
				elem.attr({"width": lastTrash.to.width, "height": lastTrash.to.height});
				updateholder(elem);
				break;
			case "cleanup":
				var len = sketches.length
				for (var i = 0; i < len; i++) {
					if (sketches[i].holderId) {
						p.getById(sketches[i].holderId).hide();
						imageSet.exclude(sketches[i]);
					}
					sketches[i].remove();
				};
				sketches.clear();
				break;
			}
			operate.push(lastTrash);
		}
	}

	option['updateimage_start'] = function (param) {
		//var elem = p.getById(param.id);
		//var holder = p.getById(elem.holderId);
		//holder.lastMatrix = holder.matrix;
		//elem.lastMatrix = elem.matrix;
		//elem.lastX = param.x;
		//elem.lastY = param.y;
		//var box = elem.getBBox();
		//elem.ox = box.x;
		//elem.oy = box.y;
		//elem.owidth = box.width;
		//elem.oheight = box.height;
		var elem = p.getById(param.id);
		elem.lastHeight = elem.attr("height");
		elem.lastWidth = elem.attr("width");
		var box = elem.getBBox();
		elem.ox = box.x;
		elem.oy = box.y;
		console.log("x", box.x, "y",box.y, "orx", elem.attr("x"), "ory", elem.attr("y"));
	}
	
	option['updateimage_move'] = function (param) {
		//var elem = p.getById(param.id);
		//var holder = p.getById(elem.holderId);
		//if (param.x < elem.ox + 30) {
			//param.x = elem.ox + 30;
		//}
		//if (param.y < elem.oy + 30) {
			//param.y = elem.oy + 30;
		//}
		//holder.transform("t" + (param.x - elem.lastX) + "," + (param.y - elem.lastY) + "...");
		//elem.transform("s"+elem.owidth / (elem.lastX - elem.ox)+','+elem.oheight / (elem.lastY - elem.oy)+','+elem.ox+','+elem.oy+"...");
		//elem.transform("s"+(param.x - elem.ox) / elem.owidth+','+(param.y - elem.oy) / elem.oheight+','+elem.ox+','+elem.oy+"...");
		//elem.lastX = param.x;
		//elem.lastY = param.y;

		console.log("updateimage_move");
		var elem = p.getById(param.id);
		var holder = p.getById(elem.holderId);
		if (param.x < elem.ox + holder.attr("r")|| param.y < elem.oy + holder.attr("r")) {
			option['updateimage_end'](param);
			return;
		}
		holder.attr({"cx": param.x, "cy": param.y});
		elem.attr({"width": param.x - elem.ox, "height": param.y - elem.oy});
	}

	option['updateimage_end'] = function (param) {
		//var image = p.getById(param.id);
		//var holder = p.getById(image.holderId);
		//pushNewOperate({operation: "updateimage", id: image.id, array_id: operate.count++, from: image.lastMatrix, to: image.matrix, holderFrom: holder.lastMatrix, holderTo: holder.matrix});

		var image = p.getById(param.id);
		pushNewOperate({operation: "updateimage", id: param.id, array_id: operate.count++, from: {width: image.lastWidth, height: image.lastHeight}, to: {width: image.attr('width'), height: image.attr('height')}});
	}

	option['gesture_start'] = function (param) {
		gestures[param.id].mouseX = param.x;
		gestures[param.id].mouseY = param.y;
		gestures[param.id].rightMouseDown();
	}

	option['gesture_move'] = function (param) {
		gestures[param.id].mouseX = param.x;
		gestures[param.id].mouseY = param.y;
		gestures[param.id].mouseMove();
	}
	option['gesture_end'] = function (param) {
		gestures[param.id].rightMouseUp();
	}

	option['session_finish'] = function (param) {
		TINY.box.show({html:'已经结束了'})
	}

/**********************************************************************************/
/*                                       GESTURE                                  */
/**********************************************************************************/

//Gesture need the following things
	function gesture(id) {
		this.id = id;
		this.circles = [];
		this.lines = [];
		this.options = [];
		this.MinBallSize = 7;
		this.Balls = 50;
		this.Lines = 49;
		this.SizeGap = 0.15;
		this.MaxAnimationMs = 14;
		this.AnimationMsGap = .5;
		this.CatchIntervalTime = 10;
		this.FadeIntervalTime = 30;
		this.DAlpha = 0.05;
		this.CatchWhich = 5;
		this.Easing = "Ease in";
		if (isiPad) {
			this.MinBallSize = 9;
			this.Balls = 2;
			this.Lines = 1;
			this.SizeGap = 2;
			this.CatchIntervalTime = 20;
			this.CatchWhich = 1;
			this.DAlpha = 0.2;
			this.FadeIntervalTime = 50;
			this.Easing = "";
		}
		var self = this;
		this.initiate = function() {
			for (var i = 0; i < this.Balls; i++) {
				this.circles[i] = p.circle(320, 240, this.MinBallSize + this.SizeGap * i);
				this.circles[i].hide();
			}
			for (var i = 0; i < this.Lines; i++) {
				this.lines[i] = p.path("M320 240L320 240").attr({"stroke-width": (this.MinBallSize + this.SizeGap * i) * 2, "stroke-linecap": "round", "stroke": getColorViaId(this.id)});
				this.lines[i].attr("opacity", 0);
			}
			this.mouseX = null;
			this.mouseY = null;
			this.catchInterval = null;
			this.fadeInterval = null;
			this.intervalSet = false;
			this.isGesturing = false;
			this.isFading = false;
		}
		this.options["catchMouse"] = function() {
			if (Math.abs(self.circles[0].attr("cx") - self.mouseX) + Math.abs(self.circles[0].attr("cy") - self.mouseY) < 1) {
				self.intervalSet = false;
				clearInterval(self.catchInterval);
			} else {
				for (var i = 0; i < self.Balls; i++) {
					if (i >= self.Balls - self.CatchWhich) {
						self.circles[i].stop().animate({
							cx: self.mouseX, cy: self.mouseY, easing: self.Easing}, self.MaxAnimationMs - i * self.AnimationMsGap);
					} else {
						self.circles[i].stop().animate({
							cx: self.circles[i + self.CatchWhich].attr("cx"), cy: self.circles[i + self.CatchWhich].attr("cy"), easing: self.Easing}, self.MaxAnimationMs - i * self.AnimationMsGap);
					}
					if (i < self.Lines) {
						self.lines[i].attr("path", "M" + self.circles[i].attr("cx") + " " + self.circles[i].attr("cy") + "L" + self.circles[i + 1].attr("cx") + " " + self.circles[i + 1].attr("cy"));
					}
				}
			}
			p.safari();
		}

		this.telesportToMouse = function() {
			for (var i = 0; i < this.Balls; i++) {
				this.circles[i].attr("cx", this.mouseX).attr("cy", this.mouseY);
				if (i < this.Lines) {
					this.lines[i].attr("path", "M" + this.circles[i].attr("cx") + " " + this.circles[i].attr("cy") + "L" + this.circles[i].attr("cx") + " " + this.circles[i].attr("cy"));
				}
			}
		}

		this.hideLines = function() {
			if (this.isFading) {
				clearInterval(this.fadeInterval);
			}
			this.fadeInterval = setInterval(this.callFunction, this.FadeIntervalTime, this.id, "fadeOutLines");
			this.isFading = true;
		}

		this.showLines = function() {
			if (this.isFading) {
				clearInterval(this.fadeInterval);
			}
			this.fadeInterval = setInterval(this.callFunction, this.FadeIntervalTime, this.id, "fadeInLines");
			this.isFading = true;
		}

		this.toFront = function() {
			for (var i = 0; i < this.circles.length; i++) {
				this.circles[i].toFront();
			};
			for (var i = 0; i < this.lines.length; i++) {
				 this.lines[i].toFront();
			};
		}
		this.options["fadeInLines"] = function() {
			var finished = false;
			for (var i = 0; i < self.Lines; i++) {
				self.lines[i].attr("opacity", self.lines[i].attr("opacity") + self.DAlpha);
				if (self.lines[i].attr("opacity") > 1) {
					self.lines[i].attr("opacity", 1);
					finished = true;
				}
			}
			if (finished) {
				clearInterval(self.fadeInterval);
				self.isFading = false;
			}
		}
		this.options["fadeOutLines"] = function() {
			var finished = false;
			for (var i = 0; i < self.Lines; i++) {
				self.lines[i].attr("opacity", self.lines[i].attr("opacity") - self.DAlpha);
				if (self.lines[i].attr("opacity") < 0) {
					self.lines[i].attr("opacity", 0);
					finished = true;
				}
			}
			if (finished) {
				clearInterval(self.fadeInterval);
				self.isFading = false;
			}
		}
		this.rightMouseDown = function() {
			this.isGesturing = true;
			this.telesportToMouse();
			this.showLines();
			if (!this.intervalSet) {
				this.intervalSet = true;
				this.catchInterval = setInterval(this.callFunction,  this.CatchIntervalTime, this.id, "catchMouse");
			}
		}
		this.mouseMove = function() {
			if (this.isGesturing) {
				if (!this.intervalSet) {
					this.intervalSet = true;
					this.catchInterval = setInterval(this.callFunction, this.CatchIntervalTime, this.id, "catchMouse");
				}
			}
		}
		this.rightMouseUp = function() {
			this.isGesturing = false;
			this.hideLines();
		}
		this.callFunction = function(id, targetOption) {
			gestures[id].options[targetOption]();
		}
	}
	
	function getColorViaId(id) {
		var colors = {
			0: "#3333cc",
			1: "#cccc33",
			2: "#eeee33",
			3: "#cc33cc"
		}
		return colors[id % 4];
	}
/**********************************************************************************/
/*                                   CUSTOM ELEMENTS                              */
/**********************************************************************************/
	// line and arrows
	function createarrow(self, x1, y1, x2, y2, size, type) {
		if (size === undefined) {
			size = 10;
		}
		var angle = Raphael.angle(x1, y1, x2, y2);
		var a45   = Raphael.rad(angle-60);
		var a45m  = Raphael.rad(angle+60);
		var a135  = Raphael.rad(angle-120);
		var a135m = Raphael.rad(angle+120);
		var a180  = Raphael.rad(angle+180);
		var x1a = x1 + Math.cos(a135) * size;
		var y1a = y1 + Math.sin(a135) * size;
		var x1b = x1 + Math.cos(a135m) * size;
		var y1b = y1 + Math.sin(a135m) * size;
		var x1c = x1 - Math.cos(a180) * size;
		var y1c = y1 - Math.sin(a180) * size;
		var x2a = x2 + Math.cos(a45) * size;
		var y2a = y2 + Math.sin(a45) * size;
		var x2b = x2 + Math.cos(a45m) * size;
		var y2b = y2 + Math.sin(a45m) * size;
		var x2c = x2 + Math.cos(a180) * size;
		var y2c = y2 + Math.sin(a180) * size;
		var linepath = "M" + x1 + " " + y1 + "L" + x2 + " " + y2;
		var arrowpath1 = "M" + x1 + " " + y1 + "L" + x1a + " " + y1a + "L" + x1c + " " + y1c + "L" + x1b + " " + y1b + "L" + x1 + " " + y1;
		var arrowpath2 = "M" + x2 + " " + y2 + "L" + x2a + " " + y2a + "L" + x2c + " " + y2c + "L" + x2b + " " + y2b + "L" + x2 + " " + y2;
		if (type === 0){
			return linepath;
		}
		else if (type === 1) {
			return linepath+" "+arrowpath2;
		}
		else {
			return linepath+" "+arrowpath2+" "+arrowpath1;
		}
	}
	Raphael.fn.singlearrow = function(x1, y1, x2, y2, width, color, size) {
		return this.path(createarrow(this, x1, y1, x2, y2, size, 1)).attr("stroke",color).attr("stroke-width", width).attr("fill", color);
	}
	Raphael.fn.doublearrow= function(x1, y1, x2, y2, width, color, size) {
		return this.path(createarrow(this, x1, y1, x2, y2, size, 2)).attr("stroke",color).attr("stroke-width", width).attr("fill", color);;
	}

	Raphael.fn.line= function(x1, y1, x2, y2, width, color) {
		return this.path(createarrow(this, x1, y1, x2, y2 , 0, 0)).attr("stroke",color).attr("stroke-width", width).attr("fill", color);;
	}

	Raphael.el.singlearrowmove = function(x1, y1, x2, y2, size) {
		this.attr('path',createarrow(this, x1, y1, x2, y2, size, 1));
	}
	Raphael.el.linemove = function(x1, y1, x2, y2, size) {
		this.attr('path',createarrow(this, x1, y1, x2, y2, size, 0));
	}
	Raphael.el.doublearrowmove = function(x1, y1, x2, y2, size) {
		this.attr('path',createarrow(this, x1, y1, x2, y2, size, 2));
	}


/**********************************************************************************/
/*                                      TIPS                                      */
/**********************************************************************************/
	//var inputNicknameModal = document.getElementById("inputNicknameModal");
	//$("#inputNicknameSubmit").click(function(){
		//if ($("#nicknameText").val() != "") {
			//TINY.box.hide();
			//TINY.box.show({html:"http://"+window.location.host+'/'+type+'/'+sessionid, opacity:0, top:-14, left:-17, boxid:"success", animate:false, close:false})
		//}
	//});
	//if (nickname == "") {
		//TINY.box.show({html: inputNicknameModal, close: false, mask: true, 
			//closejs: function() {
				//nickname = $("#nicknameText").val() + "-游客";
		//}, mclosejs: function() {
			//return;
		//}, openjs: function() {
		//}});
	//}
	//else {
		TINY.box.show({html:"http://"+window.location.host+'/'+type+'/'+sessionid, opacity:0, top:-14, left:-17, boxid:"success", animate:false, close:false})
	//}


/**********************************************************************************/
/*                                     PLAYER                                     */
/**********************************************************************************/
	function animation_play(animation){
		if (!hasclosed) {
			var onmessage_handler = s.onmessage;
			s.onmessage = function(e){
				animation.append([0,e.data])
			}
			for (var i = 0; i < animation.length; i++) {
				message_handler(JSON.parse(animation[i][1]))
			};
			s.onmessage = onmessage_handler;
		}else{
			var l = animation.length-1;
			function iteration_play(i){
				setTimeout(function(){
					message_handler(JSON.parse(animation[i][1]))
					t = animation[i][0];
					if (i<l) iteration_play(i+1)
					else {
						//FIXME FOR ANIMATION FINISH
					}
				},animation[i][0])
			}
			iteration_play(0);
		}
	}

	animation_play(JSON.parse(past_animation));

	window.onbeforeunload = function() {
		if (isRawExit) {
			return '如果您直接退出，您的大作将不会被保存为图片上传。您可以点击画面右上角的“结束”按钮来确保您的大作被保存。';
		}
	};
})
